<template>
  <Dropdown
    trigger="custom"
    :visible="visible"
    @on-clickoutside="clickOutside"
    placement="bottom-start"
    transfer
  >
    <Input
      ref="numberInput"
      :value="inputValue"
      icon="ios-calculator"
      @on-change="onChange"
      @on-blur="onBlur"
      @on-focus="onFocus"
      @on-click="openCalculator"
      @on-enter="onEnter"
      @mouseleave="onMouseleave"
      @mouseenter="onMouseenter"
      :disabled="!!disabled"
      :class="'align-' + align"
      :placeholder="placeholder"
    />
    <DropdownMenu slot="list">
      <div class="input-calculator">
        <div class="input-calculator-row">
          <input ref="calcInput" @keydown="calcInputKeyDown" />
        </div>
        <div class="input-calculator-row number-row">
          <button @click="o" data="<--">&#8672;</button
          ><button @click="o" data="C">
            C
          </button>
        </div>
        <div class="input-calculator-row number-row">
          <button @click="o" data="7">7</button
          ><button @click="o" data="8">8</button
          ><button @click="o" data="9">9</button
          ><button
            :class="[storedOp === '/' ? 'op-hold' : '']"
            @click="o"
            data="/"
          >
            &divide;</button
          ><button @click="o" data="Sqrt">Sqrt</button>
        </div>
        <div class="input-calculator-row number-row">
          <button @click="o" data="4">4</button
          ><button @click="o" data="5">5</button
          ><button @click="o" data="6">6</button
          ><button
            :class="[storedOp === '*' ? 'op-hold' : '']"
            @click="o"
            data="*"
          >
            &times;</button
          ><button @click="o" data="1/X">1/X</button>
        </div>
        <div class="input-calculator-row number-row">
          <button @click="o" data="1">1</button
          ><button @click="o" data="2">2</button
          ><button @click="o" data="3">3</button
          ><button
            :class="[storedOp === '-' ? 'op-hold' : '']"
            @click="o"
            data="-"
          >
            -</button
          ><button @click="o" data="%">%</button>
        </div>
        <div class="input-calculator-row number-row">
          <button @click="o" data="0">0</button
          ><button @click="o" data="00">00</button
          ><button @click="o" data=".">.</button
          ><button
            :class="[storedOp === '+' ? 'op-hold' : '']"
            @click="o"
            data="+"
          >
            +</button
          ><button @click="o" data="=">=</button>
        </div>
        <div class="input-calculator-row">
          <button
            class="input-calculator-confirm cancel"
            @click="closeCalculator"
          >
            <!-- 取消 -->
            {{ $srv.getI18nByPath('others.cancel') }}
          </button>
          <button @click="saveAndClose" class="input-calculator-confirm ok">
            <!-- 确定 -->
            {{ $srv.getI18nByPath('others.ok') }}
          </button>
        </div>
      </div>
    </DropdownMenu>
  </Dropdown>
</template>
<script lang="ts">
import { Component, Vue, Prop, Watch, Model } from 'vue-property-decorator';
import { Input } from 'view-design';
import { Tsrv, T_srv } from '@/services';

@Component
export default class oNumberInputEdit extends Vue {
  /**
   * props
   */

  @Prop() disabled: boolean;

  @Prop() align: string;

  // 是否disable
  @T_srv.prop({ required: false, default: '' }) placeholder: string;

  @Model('valueChange') value: string;

  private inputValue = null;

  private visible = false;

  private storedRes = null;

  private storedOp = null;

  private isNxt = false;

  private calc = null;

  private numberInput = null;

  /**
   * data
   */
  @Watch('value')
  onValueChange(val) {
    this.setValue(val);
  }

  mounted() {
    this.numberInput = this.$refs.numberInput as HTMLInputElement;
    this.calc = this.$refs.calcInput as HTMLInputElement;
    this.addCustomEvents();
  }

  public focus() {
    (this.$refs.numberInput as Input).focus();
    ((this.$refs.numberInput as Input).$el as HTMLInputElement).click();
  }

  resetValue() {
    this.inputValue = null;
  }

  private clickOutside(e) {
    // let flag = true;
    // for (let i = 0; i < 3; i++) {
    //   if (e.path[i].className === 'input-calculator') {
    //     flag = false;
    //     break;
    //   }
    // }
    // if (flag) this.closeCalculator();
  }

  public setValue(val: null | string | undefined = undefined) {
    this.inputValue = val === undefined ? null : val;
  }

  setTableValue(val: string | null) {
    this.inputValue = val;
    this.$emit('valueChange', val);
    this.$emit('on-change', val);
  }

  openCalculator() {
    if (!this.disabled) {
      this.calc.value = this.inputValue;
      this.visible = true;
      this.$emit('on-click', true);
      this.$nextTick(() => {
        this.calc.focus();
        window.addEventListener('keydown', this.onKeyDown, true);
      });
    }
  }

  closeCalculator() {
    this.visible = false;
    this.storedRes = null;
    this.storedOp = null;
    this.isNxt = false;
    window.removeEventListener('keydown', this.onKeyDown, true);
  }

  saveAndClose() {
    this.getRes(this.calc.value);
    this.setTableValue(this.calc.value);
    this.$emit('on-dochange', this.calc.value);
    this.closeCalculator();
  }

  doInput(val) {
    // 如果已经准备好输入下个值
    if (this.isNxt) {
      this.calc.value = '';
      this.isNxt = false;
    }
    this.calc.value += val;
  }

  doDelete() {
    // 如果已经准备好输入下个值
    if (this.isNxt) {
      this.storedOp = null; // 删除当前操作符
      this.isNxt = false;
    } else {
      this.calc.value = this.calc.value.slice(0, this.calc.value.length - 1);
    }
  }

  doOp(val) {
    // 如果没有值和操作符
    if (this.storedRes === null || this.storedOp === null) {
      this.storeResAndOp(val); // 则数据和操作符
    } else {
      // 如果已经准备好输入下个值，则更换操作符
      if (this.isNxt) {
        this.storedOp = val;
      } else {
        this.getRes();
        this.storeResAndOp(val);
      }
    }
  }

  onKeyDown(e) {
    e.preventDefault();

    const inputKey = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.'];
    const backKey = ['Backspace', 'Delete'];
    const opKey = ['+', '-', '*', '/', 'Enter', '='];
    const keyArr = inputKey.concat(backKey, opKey);
    // 是否为有效按键
    if (keyArr.indexOf(e.key) > -1) {
      // 是否为输入按键
      if (inputKey.indexOf(e.key) > -1) {
        this.doInput(e.key);
      } else if (backKey.indexOf(e.key) > -1) {
        this.doDelete();
      } else {
        if (['+', '-', '*', '/'].indexOf(e.key) > -1) {
          this.doOp(e.key);
        } else if (e.key === 'Enter') {
          this.saveAndClose();
        } else if (e.key === '=') {
          this.getRes(this.calc.value);
        }
      }
    }
  }

  // 将空转化为0
  p(val) {
    if (val === '') {
      return '0';
    }
    return val;
  }

  // 按键事件
  o(e) {
    e.preventDefault();

    const d = e.target.attributes.data.value;
    if (
      ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '00', '.'].indexOf(d) >
      -1
    ) {
      this.doInput(d);
    } else if (['+', '-', '*', '/'].indexOf(d) > -1) {
      this.doOp(d);
    } else if (d === '=') {
      if (this.storedRes !== null && this.storedOp !== null) {
        this.getRes();
        this.storedRes = null;
        this.storedOp = null;
        this.isNxt = false;
      }
    } else if (d === '%') {
      this.getRes(Tsrv.utils.toNumber(this.calc.value) / 100);
    } else if (d === '1/X') {
      this.getRes(1 / Tsrv.utils.toNumber(this.calc.value));
    } else if (d === 'Sqrt') {
      this.getRes(Math.sqrt(Tsrv.utils.toNumber(this.calc.value)));
    } else if (d === 'C') {
      this.calc.value = '';
      this.storedRes = null;
      this.storedOp = null;
      this.isNxt = false;
    } else if (d === '<--') {
      this.doDelete();
    }
  }

  // 保存第一个数据和操作符
  storeResAndOp(op) {
    this.storedRes = this.p(this.calc.value);
    this.storedOp = op;
    this.isNxt = true;
  }

  // 获得结果
  getRes(val: number | undefined = undefined) {
    if (this.storedRes !== null && this.storedOp !== null) {
      this.calc.value = this.doCalc(
        this.storedRes,
        this.storedOp,
        val || this.calc.value
      );
      this.storedRes = null;
      this.storedOp = null;
    } else {
      this.calc.value = val;
    }
  }

  // 计算
  doCalc(val1, op, val2) {
    // eslint-disable-next-line no-eval
    const calcRes = eval(this.p(val1) + op + this.p(val2));
    return calcRes;
  }

  calcInputKeyDown(e) {
    e.preventDefault();
  }

  onBlur() {
    window.removeEventListener('keydown', this.onKeyDown, true);
    this.$emit('on-blur', true);
  }

  onFocus() {
    this.$emit('on-focus', true);
  }

  onChange(e) {
    const val = e.target.value;
    if (val === '') {
      this.setTableValue(null);
    } else {
      this.setTableValue(val);
    }
  }

  // 添加事件
  onEnter(e) {
    this.$emit('on-enter', true);
  }

  onMouseleave(e) {
    this.$emit('on-mouseleave', true);
  }

  onMouseenter(e) {
    this.$emit('on-mouseenter', true);
  }

  private addCustomEvents() {
    ((this.$refs.numberInput as Input)
      .$el as HTMLInputElement).addEventListener(
      'mouseenter',
      this.onMouseenter
    );
    ((this.$refs.numberInput as Input)
      .$el as HTMLInputElement).addEventListener(
      'mouseleave',
      this.onMouseleave
    );
  }

  private destroyCustomEvents() {
    ((this.$refs.numberInput as Input)
      .$el as HTMLInputElement).removeEventListener(
      'mouseenter',
      this.onMouseenter
    );
    ((this.$refs.numberInput as Input)
      .$el as HTMLInputElement).removeEventListener(
      'mouseleave',
      this.onMouseleave
    );
  }

  beforeDestroy() {
    window.removeEventListener('keydown', this.onKeyDown, true);
    this.destroyCustomEvents();
  }
}
</script>
<style scoped lang="scss">
.ivu-dropdown::v-deep {
  .ivu-select-dropdown {
    width: 200px;
    height: 260p;
  }
}
.input-calculator {
  padding: 0;
  width: 200px;
  height: 260px;
  display: flex;
  flex-direction: column;
  .input-calculator-row {
    flex: 1;
    display: flex;
    flex-direction: row;
    justify-content: flex-end;
    align-items: flex-end;
    &.number-row {
      padding: 0 5px;
    }
    input {
      border: 0;
      background-color: aliceblue;
      border-bottom: 2px solid var(--default-hover-font-color);
      width: 100% !important;
      height: 100%;
      outline: none;
      padding: 0 15px !important;
      text-align: right;
    }
    button {
      border: 0;
      outline: none;
      background-color: #fff;
      width: 20%;
      height: 100%;
      cursor: pointer;
      transition: var(--animation-hover);
      border-radius: var(--default-border-radius-base);
      &:hover {
        background-color: var(--default-comp-hover-bgColor);
      }
      &.op-hold {
        background-color: aliceblue !important;
      }
      &.input-calculator-confirm {
        height: 24px;
        width: 40px;
        margin: 4px 5px 4px 8px;
        &.ok {
          background-color: var(--default-hover-font-color);
          color: #fff;
        }
      }
    }
  }
}
</style>
